/*
 * SIT_Tab.c : split interface into multiple panes.
 *
 * Written by T.Pierron, June 2009.
 */

#include "SIT_P.h"
#include "graphics.h"
#include <stdio.h>
#include <stdarg.h>

	TagList TabClass[] = {
		{ SIT_SuperClass,  NULL,          ___, SIT_INT,  (ULONG) WidgetClass },
		{ SIT_TabStyle,    "tabStyle",    __G, SIT_INT,  OFFSET(SIT_Tab, st_TabStyle) },
		{ SIT_TabCount,    "tabCount",    __G, SIT_INT,  OFFSET(SIT_Tab, st_NbTab) },
		{ SIT_TabStr,      "tabStr",      _SG, SIT_STR,  OFFSET(SIT_Tab, st_TabStr) },
		{ SIT_TabInsertAt, "tabInsertAt", _SG, SIT_INT,  OFFSET(SIT_Tab, st_TabIndex) },
		{ SIT_TabActive,   "tabActive",   _SG, SIT_INT,  OFFSET(SIT_Tab, st_CurTab) },
		{ SIT_TabAutoHide, "tabAutoHide", CSG, SIT_BOOL, OFFSET(SIT_Tab, st_AutoHide) },
		{ SIT_TabClipChld, "tabClipChld", C__, SIT_BOOL, OFFSET(SIT_Tab, st_ClipChld) },
		{ SIT_TabNameArg,  NULL,          CSG, SIT_PTR,  0 },
		{ SIT_TabIconArg,  NULL,          CSG, SIT_PTR,  0 },
		{ SIT_TagEnd }
	};

void SIT_TitleHeight(SIT_Widget, LPWSTR str, SIZE *);

static inline Bool IsWndVisible(HWND wnd)
{
	return (GetWindowLong(wnd, GWL_STYLE) & WS_VISIBLE) > 0;
}

static void SIT_AutoManageChildren(SIT_Widget w)
{
	SIT_Widget c;

	for (c = HEAD(w->sw_Children); c; NEXT(c))
	{
		if (w->sw_Visible != IsWndVisible(c->sw_Handle))
		{
			c->sw_Visible = w->sw_Visible;
			ShowWindow(c->sw_Handle, w->sw_Visible ? SW_SHOW : SW_HIDE);

			if (c->sw_Children.lh_Count > 0 && ! c->sw_SuperProc)
				SIT_AutoManageChildren(c);
		}
	}
}

static int SIT_TabAutoManage(SIT_Widget w, APTR cd, APTR ud)
{
	SIT_Widget c;
	SIT_Tab    tab = (APTR) w;
	int        nth = tab->st_CurTab = (ULONG) cd;

	for (c = HEAD(w->sw_Children); c; NEXT(c))
	{
		int n = (c->sw_TabOrder >> 16) - 1;
		c->sw_Visible = (n < 0 || n == nth);
		if (c->sw_Visible != IsWndVisible(c->sw_Handle))
		{
			ShowWindow(c->sw_Handle, c->sw_Visible ? SW_SHOW : SW_HIDE);

			if (c->sw_Children.lh_Count > 0 && ! c->sw_SuperProc)
				SIT_AutoManageChildren(c);
		}
	}

	/*
	 * Hidden tab content is not rearranged when window size change. Check if we need
	 * to recalc layout for tab that has just been selected.
	 */
	TCITEM tci  = {.mask = TCIF_PARAM};
	ULONG  sz[] = {w->sw_Box.width - w->sw_Box.x, w->sw_Box.height - w->sw_Box.y};

	TabCtrl_GetItem(w->sw_Handle, tab->st_CurTab, &tci);

	if (sz[0] != (tci.lParam & 0xffff) || sz[1] != (tci.lParam >> 16))
	{
/*		fprintf(stderr, "*** recomputing layout of tab %d: %d != %d || %d != %d\n", tab->st_CurTab,
			sz[0], tci.lParam & 0xffff, sz[1], tci.lParam >> 16); */
		SIT_LayoutWidgets(w, KeepDialogSize);
		SIT_MoveWidgets(w);
		tci.lParam = sz[0] | (sz[1] << 16);
		TabCtrl_SetItem(w->sw_Handle, tab->st_CurTab, &tci);
	}
	return 1;
}

/*
 * Store the size for which the current tab has been layed out. If tab control's box ever
 * change after that, we'll be able a rearange children when tab become active again.
 */
static int SIT_TabSetSize(SIT_Widget w, APTR cd, APTR ud)
{
	#define	sz    ((ULONG *)cd)
	TCITEM tci = {.mask = TCIF_PARAM, .lParam = sz[0] | (sz[1] << 16)};

	TabCtrl_SetItem(w->sw_Handle, ((SIT_Tab)w)->st_CurTab, &tci);

	return 0;
	#undef	sz
}

static int SIT_MeasureTab(SIT_Widget w, APTR cd, APTR ud)
{
	SIT_Tab tab = (SIT_Tab) w;
	SIZE *  sz  = cd;
	int     i, n, nb;

	#if 0
	if (ud)
	{
		RECT rect = {.right = w->sw_Box.width - w->sw_Box.x - w->sw_Margins[0] - w->sw_Margins[2] + 10, .bottom = w->sw_OptimalBox.cy};

        SetWindowPos(w->sw_Handle, NULL, 0, 0, w->sw_Box.width - w->sw_Box.x, w->sw_Box.height - w->sw_Box.y,
			SWP_NOMOVE|SWP_NOZORDER);
		TabCtrl_AdjustRect(w->sw_Handle, True, &rect);
		rect.top = -rect.top+5;

		if (w->sw_Margins[1] != rect.top)
		{
			fprintf(stderr, "Reflowing tab '%S' old top: %d, new: %ld\n", w->sw_Name, w->sw_Margins[1], rect.top);
			w->sw_Margins[1] = rect.top;
			return 1;
		}
		return 0;
	}
	#endif

	for (i = n = 0, nb = tab->st_NbTab; i < nb; i ++)
	{
		RECT rect;
		TabCtrl_GetItemRect(w->sw_Handle, i, &rect);
		n += rect.right - rect.left;
	}
	sz->cx = n+6;

	/* Layout children */
	for (i = 0; i < nb; i ++)
	{
		SIT_Widget c;
		for (c = HEAD(w->sw_Children); c; NEXT(c))
		{
			n = (c->sw_TabOrder >> 16) - 1;
			c->sw_Visible = (n < 0 || n == i);
		}

		if (SIT_LayoutWidgets(w, FitUsingOptimalBox))
		{
			n = w->sw_Box.width  - w->sw_Box.x; if (sz->cx < n) sz->cx = n;
			n = w->sw_Box.height - w->sw_Box.y; if (sz->cy < n) sz->cy = n;
		}
		memset(&w->sw_Box, 0, sizeof w->sw_Box);
	}

	/* Restore Visible flags */
	SIT_TabAutoManage(w, (APTR) tab->st_CurTab, (APTR) True);

	if (tab->st_TabStyle == SITV_TabMultiLine)
	{
		RECT rect = {.right = w->sw_OptimalBox.cx, .bottom = w->sw_OptimalBox.cy};

		// LOLWUT: have to set size of tab control to get something meaningful from AdjustRect().
        SetWindowPos(w->sw_Handle, NULL, 0, 0, rect.right, rect.bottom, SWP_NOMOVE|SWP_NOZORDER);
		TabCtrl_AdjustRect(w->sw_Handle, True, &rect);
		w->sw_Margins[0] = -rect.left + 5;
		w->sw_Margins[1] = -rect.top + 5;
		w->sw_Margins[2] = rect.right - w->sw_OptimalBox.cx + 5;
		w->sw_Margins[3] = rect.bottom - w->sw_OptimalBox.cy + 5;

		w->sw_OptimalBox.cx = rect.right - rect.left;
		w->sw_OptimalBox.cy = rect.bottom - rect.top;

		#ifdef DEBUG
		fprintf(stderr, "margins = %d,%d,%d,%d for rect %ldx%ld\n",
			w->sw_Margins[0], w->sw_Margins[1], w->sw_Margins[2], w->sw_Margins[3], rect.right, rect.bottom);
		#endif
	}
	#ifdef DEBUG
	fprintf(stderr, "*** tab size '%S' = %ldx%ld\n", w->sw_Name, sz->cx, sz->cy);
	#endif
	return 0;
}

static void SIT_AutoHideTab(SIT_Tab tab)
{
	WCHAR  buf[128];
	TCITEM tc = {.mask = TCIF_TEXT|TCIF_IMAGE, .pszText = buf, .cchTextMax = DIM(buf)};

	TabCtrl_GetItem(tab->super.sw_Handle, 0, &tc);
	tab->st_LastImgId = tc.iImage;

	int len = WideCharToMultiByte(CP_UTF8, 0, tc.pszText, -1, NULL, 0, NULL, NULL);

	if (tab->st_TabStr) free(tab->st_TabStr);
	WideCharToMultiByte(CP_UTF8, 0, tc.pszText, -1, tab->st_TabStr = malloc(len), len, NULL, NULL);
	TabCtrl_DeleteItem(tab->super.sw_Handle, 0);
	tab->st_AutoHide = 2;
}

static void SIT_SetTabs(SIT_Widget w, STRPTR tabs_utf8)
{
	SIT_Tab tab = (APTR) w;
	LPWSTR  tabs = NULL;
	TCITEM  item = {.mask = TCIF_TEXT};
	int     i = tab->st_TabIndex;

	if (tabs_utf8)
		UTF8ToUTF16(tabs_utf8, tabs);

	if (i > tab->st_NbTab)
		i = tab->st_NbTab;

//	SendMessage(w->sw_Handle, TCM_DELETEALLITEMS, 0, 0);

	while (IsDef(tabs))
	{
		LPWSTR next = wcschr(tabs, '\t');

		if (next) *next ++ = 0;
		item.pszText = tabs;

		SendMessage(w->sw_Handle, i < tab->st_NbTab ? TCM_SETITEM : TCM_INSERTITEM, i, (LPARAM) &item);
		tabs = next; i ++;
	}
	tab->st_NbTab = TabCtrl_GetItemCount(w->sw_Handle);
	if (tab->st_NbTab == 1 && tab->st_AutoHide)
		SIT_AutoHideTab(tab);
}

static void SIT_TabSetIcon(SIT_Widget w, int id, Image img)
{
	SIT_Tab tab   = (SIT_Tab) w;
	TCITEM  item  = {.mask = TCIF_IMAGE};
	int     imgid = -1;

	if (tab->st_Icons)
	{
		/* If tab already has an image associated, use its index */
		TabCtrl_GetItem(w->sw_Handle, tab->st_TabIndex, &item);
		if (item.iImage >= 0) imgid = item.iImage;
	}
	if (img)
	{
		if (! tab->st_Icons)
			TabCtrl_SetImageList(w->sw_Handle, tab->st_Icons = pImageList_Create(img->width, img->height,
				img->bpp <= 24 ? ILC_COLORDDB : ILC_COLOR32, 8, 8));

		if (imgid >= 0) pImageList_Replace(tab->st_Icons, imgid, img->hbitmap, NULL);
		else            imgid = pImageList_Add(tab->st_Icons, img->hbitmap, NULL);

		item.iImage = imgid;
	}
	else /* Remove icon from tab */
	{
		if (imgid >= 0) TabCtrl_RemoveImage(w->sw_Handle, imgid);
		item.iImage = -1;
	}
	TabCtrl_SetItem(w->sw_Handle, tab->st_TabIndex, &item);
}

static int SIT_SetTabValues(SIT_Widget w, APTR cd, APTR ud)
{
	TagList *     tag   = cd;
	SIT_Variant * value = ud;
	SIT_Tab       tab   = (SIT_Tab) w;

	switch (tag->tl_TagID) {
	case SIT_TabActive:
		if (value->integer < 0) value->integer = tab->st_NbTab-1;
		TabCtrl_SetCurSel(w->sw_Handle, tab->st_CurTab = value->integer);
		break;
	case SIT_TabStr:
		if (tab->st_TabStr) free(tab->st_TabStr);
		tab->st_TabStr = value->string;
		if (w->sw_Handle) SIT_SetTabs(w, value->string);
		break;
	case SIT_TabNameArg:
		if (tab->st_NbTab > 0 && tab->st_TabIndex < tab->st_NbTab)
		{
			STRPTR name = value->string;
			if (name == NULL) name = "";
			if (tab->st_TabIndex < 0)
				tab->st_TabIndex = tab->st_NbTab - 1;
			if (tab->st_AutoHide == 2)
			{
				/* Auto hidden, modify internal */
				tab->st_TabStr = realloc(tab->st_TabStr, strlen(name) + 1);
				strcpy(tab->st_TabStr, name);
			}
			else /* modify tab */
			{
				TCITEM item = {.mask = TCIF_TEXT};
				UTF8ToUTF16(name, item.pszText);
				TabCtrl_SetItem(w->sw_Handle, tab->st_TabIndex, &item);
			}
		}
		break;
	case SIT_TabIconArg:
		SIT_TabSetIcon(w, tab->st_TabIndex, value->pointer);
		break;
	default:
		return SIT_SetWidgetValue(w, cd, ud);
	}
	return 0;
}

typedef struct TCItem_t *   TCItem;

struct TCItem_t
{
	ListNode node;
	DWORD    lParam;
};

static TCItem ListGetNth(ListHead * first, int nth)
{
	TCItem it;
	for (it = HEAD(*first); it && nth > 0; nth --, NEXT(it));
	return it;
}

static LRESULT CALLBACK SIT_TabSubClassHandler(HWND wnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
	SIT_Widget w = (SIT_Widget) GetProp(wnd, sit_class);
	WNDPROC wproc;
	LRESULT res;
	TCItem  item;

	if (w == NULL) return DefWindowProc(wnd, msg, wParam, lParam);
	wproc = w->sw_SuperProc;

	#define	tab     ((SIT_Tab)w)
	if (msg >= TCM_FIRST && msg != WM_APP && tab->st_TabStyle != SITV_TabInvisible)
		return CallWindowProc(wproc, wnd, msg, wParam, lParam);

	switch (msg) {
#if	0
	case WM_SIZE:
//		w->sw_Flags &= ~(1 << 31);
		return CallWindowProc(wproc, wnd, msg, wParam, lParam);
	case WM_PAINT:
		if (tab->st_TabStyle == SITV_TabMultiLine && (w->sw_Flags & (1 << 31)) == 0)
		{
			/* Single-line tab doesn't have this problem BTW */
			PostMessage(w->sw_Handle, WM_APP, 0, 0);
			w->sw_Flags |= (1 << 31);
		}
		return CallWindowProc(wproc, wnd, msg, wParam, lParam);
#endif
	case WM_APP:
	{	RECT rect = {.left = w->sw_Margins[0], .top = w->sw_Margins[1], .right = w->sw_Box.width - w->sw_Box.x - w->sw_Margins[2],
	                 .bottom = w->sw_Box.height - w->sw_Box.y - w->sw_Margins[3]};
//		fprintf(stderr, "update rect ...\n");
		InvalidateRect(w->sw_Handle, &rect, FALSE);
	}	return 0;

	case WM_CTLCOLORSTATIC:
		res = SIT_SubClassHandler(wnd, msg, wParam, lParam);
		w = (SIT_Widget) GetProp((HWND) lParam, sit_class);
		/* Tab control does not set custom bg for label */
		if (w && ((w->sw_Flags & SITF_HasCustBg) || w->sw_Type != SIT_LABEL))
			return res;
		return 0;
	case WM_CTLCOLOREDIT:
	case WM_COMMAND:
		return SIT_SubClassHandler(wnd, msg, wParam, lParam);
	case WM_VSCROLL:
	case WM_HSCROLL:
		/* Up/down control of tab send this message */
		if (wnd != (HWND) lParam && GetProp((HWND) lParam, sit_class))
			return SIT_SubClassHandler(wnd, msg, wParam, lParam);
		break;

	case WM_LBUTTONDOWN:
	case WM_LBUTTONUP:
	case WM_LBUTTONDBLCLK:
	case WM_RBUTTONDOWN:
	case WM_RBUTTONUP:
	case WM_RBUTTONDBLCLK:
	case WM_MBUTTONDOWN:
	case WM_MBUTTONUP:
	case WM_MBUTTONDBLCLK:
		if (HAS_EVT(w, SITE_OnClick)) {
			TCHITTESTINFO ht;
			ht.pt.x = LOWORD(lParam);
			ht.pt.y = HIWORD(lParam);
			tab->st_TabIndex = TabCtrl_HitTest(wnd, &ht);
		}
		// no break;
	case WM_MOUSEMOVE:
		/* NM_* notifications seems useless */
		res = SIT_SubClassHandler(wnd, msg, wParam, lParam);
		if (w->sw_ButtonCapture) return res;
		break;
	case WM_CONTEXTMENU:
		return 0;

	case WM_DESTROY:
		SIT_DestroyChildren(w);
		while (tab->st_Items.lh_Count)
			free(ListRemHead(&tab->st_Items));
		SIT_DestroyWidget(w);
		break;

	/* Only if SITV_TabInvisible is set */
	case TCM_GETITEM:
	case TCM_SETITEM:
	case TCM_DELETEITEM:
		item = ListGetNth(&tab->st_Items, wParam);
		if (item == NULL) return FALSE;
		switch (msg) {
		case TCM_GETITEMW:   ((LPTCITEM)lParam)->lParam = item->lParam; break;
		case TCM_SETITEMW:   item->lParam = ((LPTCITEM)lParam)->lParam; break;
		case TCM_DELETEITEM: ListRemove(&tab->st_Items, &item->node); free(item);
		}
		return TRUE;
	case TCM_GETITEMCOUNT:
		return tab->st_Items.lh_Count;
	case TCM_SETCURSEL:
		SIT_ApplyCallback(w, (APTR) wParam, SITE_OnChanged);
		return 0;
	case TCM_INSERTITEM:
		item = ListGetNth(&tab->st_Items, wParam);
		TCItem a = malloc(sizeof *a);
		a->lParam = ((LPTCITEM)lParam)->lParam;
		if (item) ListInsert(&tab->st_Items, &a->node, &item->node);
		else ListAddTail(&tab->st_Items, &a->node);
		return TRUE;
    }
	#undef tab
    if (wproc)
		return CallWindowProc(wproc, wnd, msg, wParam, lParam);
	else
		return DefWindowProc(wnd, msg, wParam, lParam);
}

#define	DUMMYVAL   -32000

BOOL SIT_CreateTab(SIT_Widget w, va_list args)
{
	SIT_Tab tab   = (SIT_Tab) w;
	RECT    rect  = {};
	int     style = 0, i;
	uint8_t pad[] = {5, 5, 5, 5};
	HWND    hwnd;

	/* Default values */
	w->sw_SetValue = SIT_SetTabValues;
	w->sw_OptimalWidth = SIT_MeasureTab;
	w->sw_ResizePolicy = SITV_Fixed;

	/* Default margins */
	for (i = 0; i < 4; w->sw_Margins[i] = DUMMYVAL, i ++);

	SIT_ParseTags(w, args, w->sw_Class = TabClass);

	if (tab->st_TabStyle == SITV_TabMultiLine)
		style |= TCS_MULTILINE, w->sw_Flags |= SITF_AutoHeight;

	/* Win32 crapastic tab management: more often than not will cause trouble */
	if (tab->st_ClipChld) style |= WS_CLIPCHILDREN;

	if (IsVisible(w)) style |= WS_VISIBLE;
	if (HAS_TAB(w))   style |= WS_TABSTOP;

	/* If set as invisible, no control will be created */
	if (tab->st_TabStyle != SITV_TabInvisible)
	{
		hwnd = w->sw_Handle = CreateWindowEx(WS_EX_CONTROLPARENT, WC_TABCONTROL,
			w->sw_Name, WS_CHILD | style, 0, 0, 0, 0, SIT_GetContainer(w),
			(HMENU) MAKE_ID(w), instance, NULL);

		if (hwnd == NULL) return FALSE;
		SIT_SetDefaultFont(w);
		SIT_SetTabs(w, tab->st_TabStr);
		TabCtrl_SetCurSel(hwnd, tab->st_CurTab);
		w->sw_SuperProc = (APTR) GetWindowLong(w->sw_Handle, GWL_WNDPROC);
		if (tab->st_TabStyle == SITV_TabSingleLine)
		{
			TabCtrl_AdjustRect(w->sw_Handle, True, &rect);
			rect.left = - rect.left;
			rect.top  = - rect.top;
		}
	}
	else
	{
		WCHAR clsName[48];
		SIT_RegisterClass(w, clsName, 0, IDC_ARROW, NULL);
		hwnd = w->sw_Handle = CreateWindow(clsName, w->sw_Name, WS_CHILD | WS_VISIBLE,
			0, 0, 0, 0, SIT_GetContainer(w), (HMENU) MAKE_ID(w), instance, NULL);

		if (hwnd == NULL) return FALSE;
		memset(pad, 0, sizeof pad);
		SetProp(hwnd, sit_class, (HANDLE) w);
		/* This has to be set before creating tabs */
		w->sw_SuperProc = (APTR) GetWindowLong(w->sw_Handle, GWL_WNDPROC);
		SetWindowLong(w->sw_Handle, GWL_WNDPROC, (LONG) SIT_TabSubClassHandler);
		SIT_SetTabs(w, tab->st_TabStr);
	}

	int * m;
	tab->st_TabMargins = rect;
	for (i = 0, m = w->sw_Margins; i < 4; i ++, m ++) {
		if (*m == DUMMYVAL) *m = pad[i];
		*m += (&rect.left)[i];
	}
	SIT_AddCallback(w, SITE_OnResize,  SIT_TabSetSize,    NULL);
	SIT_AddCallback(w, SITE_OnChanged, SIT_TabAutoManage, NULL);

	/* Keep track of class inside callback */
	SetProp(hwnd, sit_class, (HANDLE) w);

	SetWindowLong(w->sw_Handle, GWL_WNDPROC, (LONG) SIT_TabSubClassHandler);

	return TRUE;
}

DLLIMP void SIT_TabSplice(SIT_Widget w, int pos, int del, ...)
{
	va_list args;
	SIT_Tab tab = (APTR) w;
	STRPTR  name;
	TCITEM  item = {.mask = TCIF_IMAGE};
	Bool    reflow = False;

	if (w->sw_Type != SIT_TAB) return;
	if (pos < 0) pos = tab->st_NbTab-(del>0);

	for ( ; del > 0; del --)
	{
		SIT_Widget c;
		for (c = HEAD(w->sw_Children); c; NEXT(c))
		{
			int nb = (c->sw_TabOrder >> 16) - 1;

			if (nb == pos) SIT_DestroyWidget(c); else
			if (nb >  pos) c->sw_TabOrder -= 1<<16;
		}
		TabCtrl_GetItem(w->sw_Handle, pos, &item);
		if (item.iImage >= 0 && tab->st_Icons)
			TabCtrl_RemoveImage(w->sw_Handle, item.iImage);
		TabCtrl_DeleteItem(w->sw_Handle, pos);
	}

	va_start(args, del);
	while ((name = va_arg(args, STRPTR)))
	{
		item.mask = TCIF_TEXT|TCIF_IMAGE;
		item.iImage = -1;
		if (name == (STRPTR) -1) /* Add with icon */
		{
			Image img;
			name = va_arg(args, STRPTR);
			img  = va_arg(args, Image);
			if (img)
			{
				if (! tab->st_Icons)
					TabCtrl_SetImageList(w->sw_Handle, tab->st_Icons = pImageList_Create(img->width, img->height,
						img->bpp <= 24 ? ILC_COLORDDB : ILC_COLOR32, 8, 8));

				item.iImage = pImageList_Add(tab->st_Icons, img->hbitmap, NULL);
			}
		}

		if (tab->st_AutoHide == 2)
		{
			/* Was hidden, add last item again */
			int imgid = item.iImage;
			UTF8ToUTF16(tab->st_TabStr, item.pszText);
			item.iImage = tab->st_LastImgId;
			TabCtrl_InsertItem(w->sw_Handle, pos, &item);
			tab->st_AutoHide = 1;
			reflow = True;
			item.iImage = imgid;
		}
		if (tab->st_NbTab == 0)
			reflow = True;
		UTF8ToUTF16(name, item.pszText);
		TabCtrl_InsertItem(w->sw_Handle, pos, &item); pos ++;
	}
	va_end(args);

	tab->st_NbTab = TabCtrl_GetItemCount(w->sw_Handle);

	if (tab->st_NbTab == 1 && tab->st_AutoHide == 1)
		SIT_AutoHideTab(tab), reflow = True;

	if (reflow || tab->st_TabStyle == SITV_TabMultiLine)
	{
		RECT rect = {}, old = tab->st_TabMargins;
		int  i;
		TabCtrl_AdjustRect(w->sw_Handle, True, &rect);
		rect.left = - rect.left;
		rect.top  = - rect.top;
		tab->st_TabMargins = rect;
		for (i = 0; i < 4; i ++)
			w->sw_Margins[i] += (&rect.left)[i] -= (&old.left)[i];
		w->sw_Box.width  += rect.left + rect.right;
		w->sw_Box.height += rect.top  + rect.bottom;
		w->sw_CurrentBox.cx = w->sw_Box.width  - w->sw_Box.x;
		w->sw_CurrentBox.cy = w->sw_Box.height - w->sw_Box.y;
		SIT_InitiateReflow(w);
	}
	PostMessage(w->sw_Handle, WM_APP, 0, 0);
}
